package argfunc

import (
	"fmt"
	"sort"
	"sync"
)

// kpl-plugs/status
//  plg.status 注册/status 提供操作页面

type ArgFunc = func(args string, tag interface{}) (val interface{}, err error)

type ArgFuncOption struct {
	Catalog string
	Caption string
	Tag     interface{}
	Func    ArgFunc
}

type argfuncRegisterItem struct {
	id  string
	opt ArgFuncOption
}

var (
	globalRegister = &argfuncRegister{lst: make(map[string]*argfuncRegisterItem)}
)

type ArgFuncSortField int8

const (
	ByNone ArgFuncSortField = iota
	ById
	ByCaption
)

type argfuncRegister struct {
	lk  sync.RWMutex
	lst map[string]*argfuncRegisterItem
}

func (this *argfuncRegister) Register(id string, opt ArgFuncOption) {
	this.lk.Lock()
	defer this.lk.Unlock()

	if len(opt.Caption) == 0 {
		opt.Caption = id
	}

	if itm, ok := this.lst[id]; ok {
		itm.opt = opt
	} else {
		this.lst[id] = &argfuncRegisterItem{id, opt}
	}
}

func (this *argfuncRegister) UnRegister(id string) bool {
	this.lk.Lock()
	defer this.lk.Unlock()
	if _, ok := this.lst[id]; ok {
		delete(this.lst, id)
		return true
	} else {
		return false
	}
}

func (this *argfuncRegister) GetFunc(id string) ArgFuncOption {
	this.lk.RLock()
	defer this.lk.RUnlock()
	v := this.lst[id]
	if v == nil {
		return ArgFuncOption{}
	}
	return v.opt
}

func (this *argfuncRegister) ExecuteFunc(id string, args string) (val interface{}, err error) {
	opt := this.GetFunc(id)
	if opt.Func == nil {
		return nil, fmt.Errorf("%s func not exists", id)
	}
	return opt.Func(args, opt.Tag)
}

func (this *argfuncRegister) Range(catalog string, sortby ArgFuncSortField, fn func(id string, opt ArgFuncOption) bool) {
	var lst []*argfuncRegisterItem
	this.lk.RLock()
	for _, v := range this.lst {
		if v.opt.Catalog == catalog {
			lst = append(lst, v)
		}
	}
	this.lk.RUnlock()

	if len(lst) > 0 {
		if sortby == ById {
			sort.Slice(lst, func(i, j int) bool {
				return lst[i].id < lst[j].id
			})
		} else if sortby == ByCaption {
			sort.Slice(lst, func(i, j int) bool {
				return lst[i].opt.Catalog < lst[j].opt.Catalog
			})
		}

		for _, v := range lst {
			if !fn(v.id, v.opt) {
				return
			}
		}
	}
}

func RegisterStatusFunc(id string, caption string, urlDefault string, fn func(urlArg string, tag interface{}) (val interface{}, err error)) {
	ArgFuncRegister(id, ArgFuncOption{Caption: caption, Catalog: "status", Tag: urlDefault, Func: fn})
}

func UnRegisterStatusFunc(id string) {
	ArgFuncUnRegister(id)
}

var (
	ArgFuncRegister   = globalRegister.Register
	ArgFuncUnRegister = globalRegister.UnRegister
	ArgFuncRange      = globalRegister.Range
	ArgFuncGet        = globalRegister.GetFunc
	ArgFuncExecute    = globalRegister.ExecuteFunc
)
